//
//TODO -Planned features and fixes:
//
//          - Address flashy AJAX garbage (e.g. google instant search, wikipedia donation banner,
//            BBC news ticker).
//          - Support for the following alphabets:
//              - Unifon
//              - Futhorc
//          - If possible, have a small CSS popup giving numerically listed transliteration
//            candidates to select from in the post IME.
//          - When opening pages in new tabs and the plugin is enabled, only the page
//            currently being viewed is transliterated.
//          - Remove all hard-coded UI-facing English and move it into localization DTDs.
//            as well as the dictionary editing dialog (by neccesity).
//          - Move actual transliteration off of the main thread to a worker.

var aatel = {
    init: function() {

        // https://developer.mozilla.org/en/Adding_preferences_to_an_extension
        this.prefs = Components.classes["@mozilla.org/preferences-service;1"]  
            .getService(Components.interfaces.nsIPrefService).getBranch("extensions.aatel.");  
        this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);  
        this.prefs.addObserver("", this, false);  
        this.defaultAlphabet = aatel.prefs.getCharPref("alphabet");

        /*Components.utils.import("chrome://aatel/content/js_cursor_position.js");*/
        Components.utils.import("chrome://aatel/content/alphabet/alpha.js");
        Components.utils.import("chrome://aatel/content/dict.js");

        alpha.init();
        /*maxkir.init();*/

        this.initialized = true;
        var appContent = document.getElementById("appcontent");

        // Adding AATEL button context menu handlers.
        var menuitems = document.getElementById("aatelButtonContext").children;   
        for (var i = 0; i < menuitems.length; i++) {   

            if (menuitems[i].getAttribute("class") != "alphabet") continue;
            var handler = function(e) {
                e.stopPropagation();
                aatel.switchAlphabet(this.label);
                return false;
            }
            menuitems[i].addEventListener("command", handler, true);
        }

        // Page right-click context menu entry for editing pronunciation dictionary
        function dictHandler(e) {
            e.stopPropagation();
            var text = '';
            try {
                text = content.window.getSelection().getRangeAt(0).toString();  
                text = text.match(/^.*?(\w+)/)[0]; 
            }
            catch (err) {}
            window.openDialog("chrome://aatel/content/dict.xul", "",
                              "chrome,centerscreen,resizable",
                              {term: text, alphabet: "Shavian"});
            return false;
        }
        var aatelDict = document.getElementById("aatel_dict");   
        aatelDict.addEventListener("command", dictHandler, true);

        function cmHandler(e) {
            e.stopPropagation();
            var menuItem = document.getElementById("aatel_dict");   
            var text = '';
            try {
                text = content.window.getSelection().getRangeAt(0).toString();  
                text = text.match(/^.*?(\w+)/)[0]; 
            }
            catch (err) {}
            if (text) {
                var displayText = text;
                if (text.length > 15) displayText = text.substr(0, 15);
                menuItem.label = "AATEL dictionary: \"" + displayText + "\"";
            }
            else {
                menuItem.label = "AATEL dictionary...";
            }
            return false;
        }
        var cm = document.getElementById("contentAreaContextMenu");   
        cm.addEventListener("popupshowing", cmHandler, true);

        appContent.addEventListener("DOMContentLoaded", this.onPageLoad, true); 
    },  


    onPageLoad: function() {  
        aatel.dbglog('onPageLoad, aatel.defaultAlphabet is: ' + aatel.defaultAlphabet);
        aatel.switchAlphabet( aatel.defaultAlphabet, !aatel.prefs.getBoolPref("enabled") );
        content.enablePostIME = false;
    },


    // Wrapper function around ptnInternal. This should spawn a worker thread which will wait for
    // for messages. ptnInternal will be changed to no longer update nodes itself; instead it will
    // add a reference to each text node to an array and pass the numerical index and the text itself
    // to the worker in a message. After the ptnInternal finishes, processTextNodes will wait
    // for messages back from the worker which will include the node index and transliterated text. Nodes
    // will be looked up in the previously created array and their text content will be updated.
    // TODO: Not yet finished.
    processTextNodes: function(cmd, node) {
        var worker = new Worker("chrome://aatel/content/testworker.js");
        worker.addEventListener('error', function(event) {}, false);
        worker.addEventListener('message', function(event) {  
            alert(event.data);
        }, false);

        aatel.ptnInternal(cmd, node);

        udict = aatel.getUserDict();
        var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
        udictURI = ioService.newFileURI(udict).prePath + ioService.newFileURI(udict).path;
        worker.postMessage(["Sequence of words", udictURI, content.alphabet]);
    },

    //
    // https://developer.mozilla.org/en/XUL_School/Local_Storage
    getUserDict: function() {  
        var aatelDir = Components.classes["@mozilla.org/file/directory_service;1"]
            .getService(Components.interfaces.nsIProperties)
            .get("ProfD", Components.interfaces.nsIFile);
        aatelDir.append("aatel");  
        if (!aatelDir.exists() || !aatelDir.isDirectory()) {  
            aatelDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0774);  
        }  
        aatelDir.append("userdict.json");
        var userDictionary = Components.classes["@mozilla.org/file/local;1"].  
            createInstance(Components.interfaces.nsILocalFile);  
        userDictionary.initWithPath(aatelDir.path);  

        return userDictionary;  
    },  


    // TODO: This is really ugly.
    // http://stackoverflow.com/questions/1512876/how-to-replace-text-in-html-document-without-affecting-the-markup
    // http://stackoverflow.com/questions/3883780/javascript-recursive-anonymous-function
    ptnInternal: function ptnInternal(cmd, node) { 
        node = node || content.document.body;                    // base node 
        var text;
        var children = node.childNodes, i = 0;
        while (node = children[i++]) { 
            if (node.nodeName.toUpperCase() == "SCRIPT" ||
                node.nodeName.toUpperCase() == "PRE"    ||
                node.nodeName.toUpperCase() == "CODE") continue;
            if (node.nodeType == 3 && node.textContent) {

                function swap(n) {
                    if (n.mode == content.alphabet) {
                        n.textContent = n.latin;
                        n.mode = "latin";
                    }
                    else {
                        n.textContent = n.phonetic;
                        n.mode = content.alphabet;
                    }
                }
                function genTextFunc(targetChild) {
                    return function (e) {
                        if (!e.shiftKey) return;         // only switch on shift+click
                        e.preventDefault();              // do not follow links
                        swap(targetChild);
                    }
                }

                switch (cmd) {
                    case "init":
                        node.latin = node.textContent;
                        node.phonetic = alpha.transliterate(node.textContent, content.alphabet);
                        node.mode = content.alphabet;
                        node.textContent = node.phonetic;
                        node.parentNode.removeEventListener("click", genTextFunc(node), true);
                        node.parentNode.addEventListener("click", genTextFunc(node), true);
                        break;
                    case "force-latin":
                        if (node.latin == undefined) break;
                        node.textContent = node.latin;
                        node.mode = "latin";
                        break;
                    default:
                        swap(node);
                }
            }
            else if (node.nodeType == 1 && (node.title || node.alt)) {
                if      (node.title) text = node.title;
                else if (node.alt)   text = node.alt;

                switch (cmd) {
                    case "init":
                        node.latin = text;
                        node.phonetic = alpha.transliterate(text, content.alphabet);
                        node.mode = content.alphabet;
                        text = node.phonetic;
                        break;
                    case "force-latin":
                        if (node.latin == undefined) break;
                        text = node.latin;
                        node.mode = "latin";
                        break;
                    default:
                        if (node.mode == content.alphabet) {
                            text = node.latin;
                            node.mode = "latin";
                        }
                        else {
                            text = node.phonetic;
                            node.mode = content.alphabet;
                        }
                }
                if      (node.title) node.title = text;
                else if (node.alt)   node.alt = text;
            }
            ptnInternal(cmd, node); 
        }
    }, 


    processFormsAndButtons: function(cmd) {

        function autoConvert (e) {          // textbox IME function.

            /*var span = this.parentNode.getElementsByTagName('span')[0];*/
            /*span.textContent = this.value;*/

            // TODO: temporary CSS overlay showing IME is on.
            if(e.ctrlKey && String.fromCharCode(e.which).toUpperCase() == 'M') {

                // Change field background color when IME is on.
                var color;
                if (content.enablePostIME == true)  {
                    if ( color = this.getAttribute("data-oldColor") ) {
                        this.style.backgroundColor = color;
                    } else {
                        this.style.backgroundColor = "#FFFFFF";
                    }
                    content.enablePostIME = false;
                }
                else if (content.enablePostIME == false) {
                    if ((color = this.style.backgroundColor)
                           && !this.getAttribute("data-oldColor")) {
                        this.setAttribute("data-oldColor", color);
                    }
                    this.style.backgroundColor = "#BBFDBE";
                    content.enablePostIME = true;
                }
                return;
            }
            if (!content.enablePostIME) return;

            const ignore = [8, 127, 37, 38, 39, 40];     // ignore backspace, delete, and arrow keys.
            if (ignore.indexOf(e.which) != -1) return;  
            var ch = alpha.unicode(e.which);
            if (ch.match(/[\w']/)) return;               // ignore alphabetical characters.

            // splice textarea text at cursor, if there is any contiguous latin alphabetic
            // text just before the cursor, convert it, piece everything back together and
            // replace this.value with it. 
            var front = this.value.slice(0, this.selectionEnd);
            var back  = this.value.slice(this.selectionEnd);
            var matches = front.match(/^([\s\S]*?)([\w']+)$/);
            if (matches) {
                front = matches[1] + alpha.transliterate(matches[2], content.alphabet);
                this.value = front + back;
                this.selectionEnd = front.length;           // reposition cursor
            }
        }

        var inputs = content.document.getElementsByTagName("input");   
        for (var i = 0; i < inputs.length; i++) {   
            var field = inputs[i];
            if ((field.type == "submit") || (field.type == "button")) {
                if (cmd == "force-latin") {
                    if (field.latin == undefined) break;
                    field.value = field.latin;
                    field.mode = "latin";
                }
                else {
                    field.latin = field.value;
                    field.phonetic = alpha.transliterate(field.value, content.alphabet);
                    field.mode = content.alphabet;
                    field.value = field.phonetic;
                }
            }
            else if (field.type == "text" && cmd == "init") {
                field.removeEventListener("keypress", autoConvert, false);
                field.addEventListener("keypress", autoConvert, false);
            }
        }
        var textAreas = content.document.getElementsByTagName("textarea");   
        for (var i = 0; i < textAreas.length; i++) {   
            var n = textAreas[i];
            if (n.type == "textarea" && cmd == "init") {   //Enable IME
                n.removeEventListener("keypress", autoConvert, false);
                n.addEventListener("keypress", autoConvert, false);
            }
        }
    },


    // Wrap input field with div and mirror text input to the filed so as to measure
    // X,Y coordinates of cursor.
    /*addMirrorDiv: function(field) {*/

    /*function cloneStyle(target, styleName) {*/
    /*var val = field.style[styleName];*/
    /*if (!val) {*/
    /*var css = content.document.defaultView.getComputedStyle(field, null);*/
    /*val = css ? css[styleName] : null;*/
    /*}*/
    /*if (val) {*/
    /*target.style[styleName] = val;*/
    /*}*/
    /*}*/

    /*var oldParent = field.parentNode;*/
    /*var wrapper = document.createElement('div'); */
    /*var span = document.createElement('span'); */
    /*wrapper.appendChild(field); */
    /*wrapper.appendChild(span); */
    /*oldParent.appendChild(wrapper); */

    /*cloneStyle(wrapper, 'position');*/
    /*cloneStyle(wrapper, 'top');*/
    /*cloneStyle(wrapper, 'left');*/
    /*cloneStyle(wrapper, 'fontSize');*/
    /*cloneStyle(wrapper, 'fontFamily');*/
    /*cloneStyle(wrapper, 'fontWeight');*/
    /*cloneStyle(wrapper, 'fontVariant');*/
    /*cloneStyle(wrapper, 'fontStyle');*/
    /*cloneStyle(wrapper, 'textTransform');*/
    /*cloneStyle(wrapper, 'lineHeight');*/
    /*wrapper.style.backgroundColor = "#FFFF33";*/
    /*span.style.position = "absolute";*/
    /*span.style.left = "500px";*/
    /*span.style.width = "0px";*/
    /*span.style.wordWrap = "break-word";*/
    /*},*/

    aatelButtonToggle: function (e) {
        if (!content.aatelized) {
            content.enablePostIME = false;
            aatel.prefs.setBoolPref("enabled", true);
            aatel.processTextNodes("init");
            aatel.processFormsAndButtons("init");
            content.aatelized = true;
        }
        else if (aatel.prefs.getBoolPref("enabled")) {
            aatel.prefs.setBoolPref("enabled", false);
            aatel.processTextNodes("force-latin");
            aatel.processFormsAndButtons("force-latin");
        }
        else {
            aatel.prefs.setBoolPref("enabled", true);
            aatel.processTextNodes();
            aatel.processFormsAndButtons();
        }
    },


    switchAlphabet: function(alphabet, suppressEnabling) {
        content.alphabet = alphabet;
        aatel.defaultAlphabet = alphabet;
        aatel.prefs.setCharPref("alphabet", alphabet);
        if (!suppressEnabling) {
            aatel.processTextNodes("force-latin");
            aatel.processFormsAndButtons("force-latin");
            aatel.prefs.setBoolPref("enabled", true);
            aatel.processTextNodes("init");
            aatel.processFormsAndButtons("init");
            content.aatelized = true;
        }
    },


    observe: function() {},

    dbglog: function (aMessage) {
      var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
                                     .getService(Components.interfaces.nsIConsoleService);
      consoleService.logStringMessage("AATEL: " + aMessage);
    }
};

window.addEventListener("load", function(e) { aatel.init(e); }, false); 

